/*
            %d 日期 子格式
            %T 缩进
            %t 线程id
            %p 日志级别
            %c 日志器名称
            %f 文件名
            %l 行号
            %m 日志消息
            %n 换行
*/
// 时间{时:分:秒}缩进 线程ID 缩进 [日志级别] 缩进 [日志名称] 缩进 文件名:行号 缩进 消息换行
#ifndef __FORMATTER_HPP__

#define __FORMATTER_HPP__

#include "message.hpp"
#include <iostream>
#include <memory>
#include <string>
#include <cassert>
#include <ctype.h>
#include <sstream>

namespace Log
{
    class FormatItem // 各种格式的基类
    {
    public:
        using ptr = std::shared_ptr<FormatItem>;
        virtual ~FormatItem() {}//刚才没有实现

        // 子类用于将子类格式对应的字符写入os,
        // os也是一个基类
        // 方便用于接收多种子类达到传入子类不同写入位置不同,如文件标准输出等
        virtual void format(std::ostream &os, const LogMsg &msg) = 0;
    };

    class MsgFormatItem : public FormatItem
    {
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        MsgFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            os << msg._message;
        }
    };

    class TimeFormatItem : public FormatItem
    {
    private:
        std::string _format;
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        TimeFormatItem(const std::string str = "%H:%M:%S") : _format(str){};
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            struct tm result;            // 用于存放转换后的结果
            time_t time = msg._time;     // 时间戳
            localtime_r(&time, &result); // 带_r的是线程安全
            char tmp[64];                // 存放转换为字符串的结果
            strftime(tmp, 63, _format.c_str(), &result);
            os << tmp;
        }
    };
    class LevelFormatItem : public FormatItem
    {
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        LevelFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            os << LogLevel::toString(msg._level); // 需要转字符串,直接输出的值是枚举的0,1,2等值
        }
    };

    class NameFormatItem : public FormatItem
    {
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        NameFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            os << msg._Logger;
        }
    };

    class ThreadFormatItem : public FormatItem
    {
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        ThreadFormatItem(const std::string &str = "") {}
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            os << msg._threadId;
        }
    };

    class FileFormatItem : public FormatItem
    {
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        FileFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            os << msg._file;
        }
    };

    class TableFormatItem : public FormatItem
    {
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        TableFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            os << '\t';
        }
    };

    class NFormatItem : public FormatItem
    {
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        NFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            os << "\n";
        }
    };

    class LineFormatItem : public FormatItem
    {
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        LineFormatItem(const std::string &str = ""){}
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            os << msg._line;
        }
    };

    // 其他非格式化字符
    class OtherFormatItem : public FormatItem
    {
    private:
        string _str;
    public:
        // 构造用于接收非格式化字符,有些子类不用有些用,为统一格式
        OtherFormatItem(const std::string &str = "") : _str(str){};
        virtual void format(std::ostream &os, const LogMsg &msg)
        {
            os << _str;
        }
    };

    class Format
    {
    private:
        std::string _pattern;               // 格式
        std::vector<FormatItem::ptr> _item; // 解析后的格式对象(对象里存着对应value值)
    public:
        using ptr = std::shared_ptr<Format>;
        Format(const std::string pattern = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n")
            : _pattern(pattern)
        {
            assert(parsePattern()); // 解析只能成功
        }

        std::string format(const LogMsg &msg) // 返回字符串重载
        {
            std::stringstream ss;
            for (auto& it : _item)
            {
                it->format(ss, msg); // 注意这个是非本类函数
            }
            return ss.str();
        }

        std::ostream &format(std::ostream &os, const LogMsg &msg) // 返回流重载
        {
            for (auto& it : _item)
            {
                it->format(os, msg); // 注意这个是非本类函数
            }
            return os;
        }

    private:
        FormatItem::ptr createItem(const std::string format, const std::string subformat)
        {
            if (format == "d")
                return FormatItem::ptr(new TimeFormatItem(subformat));
            else if (format == "T")
                return FormatItem::ptr(new TableFormatItem(subformat));
            else if (format == "t")
                return FormatItem::ptr(new ThreadFormatItem(subformat));
            else if (format == "p")
                return FormatItem::ptr(new LevelFormatItem(subformat));
            else if (format == "c")
                return FormatItem::ptr(new NameFormatItem(subformat));
            else if (format == "f")
                return FormatItem::ptr(new FileFormatItem(subformat));
            else if (format == "l")
                return FormatItem::ptr(new LineFormatItem(subformat));
            else if (format == "m")
                return FormatItem::ptr(new MsgFormatItem(subformat));
            else if (format == "n")
                return FormatItem::ptr(new NFormatItem(subformat));
            else
                return FormatItem::ptr(); // 没有对应则返回空
        }
        bool parsePattern()
        {
            //[%d{%H:%M:%S}][%t][%p][%c][%f:%l] %m%n"
            /*

            k值决定构造什么格式对象,v值是构造时传的参数

            k值 含义        v值
            d   日期       子格式(例如%H:%M:%S)
            T   缩进       null
            t   线程id     null
            p   日志级别   null
            c   日志器名称 null
            f   文件名     null
            l   行号       null
            m   日志消息   null
            n   换行       null
            null 非格式化字符  字符本身
            */
            if (_pattern.empty())
                return false; // 空串则解析失败
                std::string key;
                std::string value;
            for (int i = 0; i < _pattern.size(); i++)
            {
                if (_pattern[i] != '%') // 先把非格式化字符分离
                {
                    value.push_back(_pattern[i]);
                    continue;
                }
                if (i + 1 < _pattern.size() && _pattern[i + 1] == '%') // 双%%视为一个%
                {
                    value.push_back(_pattern[++i]);
                    continue;
                }
                if (value.empty() == false)
                {
                    FormatItem::ptr p(new OtherFormatItem(value));
                    _item.push_back(p);
                    value.clear();
                }
                // 到处为止非格式化字符已经处理完毕
                i++; // 当前位置指向%后的值
                if (i < _pattern.size() && isalpha(_pattern[i]))
                {
                    key.push_back(_pattern[i]);
                }
                else
                {
                    std::cout << &_pattern[i - 1] << "附近格式出错" << std::endl;
                    return false;
                }
                // 接下来查看有没有v值,也就是子格式
                if (i + 1 < _pattern.size() && _pattern[i + 1] == '{') // 有子格式
                {
                    i++;
                    while (i < _pattern.size() && _pattern[i] != '}')
                    {
                        value.push_back(_pattern[i++]);
                    }
                    if (i == _pattern.size() && _pattern[i - 1] != '}')
                    {
                        std::cout << "{}匹配出错" << std::endl;
                        return false;
                    }
                }
                FormatItem::ptr ret = createItem(key, value);
                if (ret == nullptr)
                {
                    std::cout << "没有对应格式:" << key << std::endl;
                    return false;
                }
                else
                {
                    _item.push_back(ret);
                }
                key.clear();
                value.clear();
            }
            return true;//完整解析完才算成功
        }
    };

};

#endif // !__FORMATTER_HPP__